package cn.xu.tool.big.a.compments;

import cn.xu.tool.big.a.dto.KLine;
import cn.xu.tool.big.a.dto.TotalReturn;
import cn.xu.tool.big.a.dto.TradeRecord;
import org.apache.commons.lang3.time.DateUtils;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.StandardChartTheme;
import org.jfree.chart.annotations.XYAnnotation;
import org.jfree.chart.annotations.XYPointerAnnotation;
import org.jfree.chart.annotations.XYTextAnnotation;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.DateTickMarkPosition;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.CandlestickRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.chart.ui.HorizontalAlignment;
import org.jfree.chart.ui.TextAnchor;
import org.jfree.data.Range;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.DefaultOHLCDataset;
import org.jfree.data.xy.OHLCDataItem;
import org.jfree.data.xy.OHLCDataset;
import org.jfree.data.xy.XYDataset;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;

/**
 * 类名: CombinedReportCp
 * 描述: TODO
 * 作者: ClarkXu clark2021@qq.com
 * 日期: 2025/7/9
 */
public class CombinedReportCp {


    private static final String PARENT_PATH = "D:\\work\\code\\clark\\gitee\\big_a\\datas";

    public static void createCombinedReport(List<TradeRecord> tradeRecords,
                                            TotalReturn totalReturn,
                                            List<KLine> kLines,
                                            String stockCode,
                                            String startDate,
                                            String endDate, String...dateF) throws ParseException, IOException {

        // 1. 创建两个数据集
        XYDataset assetDataset = createAssetDataset(tradeRecords, totalReturn, dateF);
        OHLCDataset kLineDataset = createKLineDataset(kLines, dateF);

        // 2. 创建组合图表
        String start = tradeRecords.get(0).getDate().replaceAll("-", "");
        String end = tradeRecords.get(tradeRecords.size()-1).getDate().replaceAll("-", "");
        String title = "资产与价格走势分析 (" + start + "-" + end + ")";
        JFreeChart chart = createCombinedChart(assetDataset, kLineDataset, title, kLines, dateF);

        applyChineseTheme(chart);

        // 3. 添加标注和主题
        addTradeAnnotations(chart, tradeRecords, dateF);

        addKLineTradeAnnotations(chart, tradeRecords, kLineDataset, dateF); // K线图标注

        // 4. 保存图表
        saveCombinedChart(stockCode, chart, startDate, endDate);
    }


    private static void addKLineTradeAnnotations(JFreeChart chart,
                                                 List<TradeRecord> tradeRecords,
                                                 OHLCDataset dataset,
                                                 String...dateF) throws ParseException {
        // 1. 获取K线图子图（组合图表的第一个子图）
        CombinedDomainXYPlot combinedPlot = (CombinedDomainXYPlot) chart.getPlot();
        XYPlot kLinePlot = (XYPlot) combinedPlot.getSubplots().get(0);

        // 2. 获取渲染器并计算价格范围
        CandlestickRenderer candleRenderer = (CandlestickRenderer) kLinePlot.getRenderer();
        Range priceRange = candleRenderer.findRangeBounds(dataset); // ✅ 修正范围获取方式
        double priceSpan = priceRange.getLength();

        // 3. 按日期分组交易记录
        Map<String, List<TradeRecord>> dateMap = new HashMap<>();
        for (TradeRecord record : tradeRecords) {
            String dateKey = record.getDate();
            if("买入".equals(record.getOperation())||"卖出".equals(record.getOperation())){
                dateMap.computeIfAbsent(dateKey, k -> new ArrayList<>()).add(record);
            }
        }

        // 4. 创建标注并添加到图表
        for (Map.Entry<String, List<TradeRecord>> entry : dateMap.entrySet()) {
            String date = entry.getKey();
            List<TradeRecord> sameDayRecords = entry.getValue();

            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
            if(dateF!=null&&dateF.length>0){
                dateFormat = new SimpleDateFormat(dateF[0]);
            }
            // 设置中国时区 (GMT+8)
            dateFormat.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 统一为北京时间
            Date recordDate = dateFormat.parse(date);
            int itemIndex = findItemIndex(dataset, recordDate, dateFormat);

            if (itemIndex >= 0) {
                // 获取当日价格数据
                double high = dataset.getHighValue(0, itemIndex);
                double low = dataset.getLowValue(0, itemIndex);

                //使用List替代不存在的XYAnnotationContainer
//                List<XYAnnotation> annotations = new ArrayList<>();

                for (int i = 0; i < sameDayRecords.size(); i++) {
                    TradeRecord record = sameDayRecords.get(i);
                    XYPointerAnnotation annotation = createKLineAnnotation(
                            record, recordDate, high, low, priceSpan, i, sameDayRecords.size()
                    );
//                    annotations.add(annotation);
                    kLinePlot.addAnnotation(annotation); // 直接添加到Plot
                }
            }
        }
    }

    // 查找日期对应的数据索引（保持不变）
    private static int findItemIndex(OHLCDataset dataset, Date targetDate, SimpleDateFormat sdf) {
        // 格式化目标日期字符串
        String targetDateStr = sdf.format(targetDate);

        for (int i = 0; i < dataset.getItemCount(0); i++) {
            // 获取时间戳并转换为Date对象
            double timestamp = dataset.getXValue(0, i);
            Date itemDate = new Date((long) timestamp);

            // 通过字符串比较日期（忽略时间部分）
            if (sdf.format(itemDate).equals(targetDateStr)) {
                return i;
            }
        }
        return -1;
    }

    private static int lengthPointer = 0;
    private final static double[] KLINE_ANNO_LENGTH = {25, 55};
    // 新增静态变量记录日期和偏移量
    private static String lastDate = null;
    private static int sameDayCount = 0;

    // 创建K线交易标注（逻辑优化）
    private static XYPointerAnnotation createKLineAnnotation(TradeRecord record,
                                                             Date date,
                                                             double high,
                                                             double low,
                                                             double priceSpan,
                                                             int index,
                                                             int total) throws ParseException {

        // 获取当前日期字符串（格式YYYY-MM-DD）
        String currentDate = record.getDate();
        // 日期判定逻辑
        if (currentDate.equals(lastDate)) {
            sameDayCount++;
        } else {
            sameDayCount = 0;
            lastDate = currentDate;
        }

        // 垂直方向基础位置（买入上浮/卖出下浮5%）
        double y = "买入".equals(record.getOperation()) ?
                high + (priceSpan * 0.05) :
                low - (priceSpan * 0.05);

        // 新增垂直调整：同日期交易交错分布
        if (sameDayCount > 0) {
            double verticalOffset = priceSpan * 0.04; // 偏移量调整系数
            if (sameDayCount % 2 == 1) {  // 奇数笔交易向上
                y += verticalOffset;
            } else {                      // 偶数笔交易向下
                y -= verticalOffset;
            }
        }

        // 水平方向偏移（不同交易日动态扩展）
        double baseOffset = total > 10 ? 25 : 15;  // 根据总数自动调整密度
        double xOffset = 0;

        // 仅当交易日不同时才偏移
        if (!currentDate.equals(lastDate) || total == 1) {
            // 对称分布算法：中间交易不动，两侧渐开
            xOffset = (index - (total-1)/2.0) * baseOffset;
        }

        double x = date.getTime() + xOffset;

        // 创建标注对象
        String symbol = "买入".equals(record.getOperation()) ? "+" : "-";
        XYPointerAnnotation annotation = new XYPointerAnnotation(
                String.format("%s %s ¥%.2f",
                        record.getDate().substring(5).replace("-", "/"),
                        symbol+record.getVolume(),
                        record.getPrice()),
                x, y,
                ANGLES[lengthPointer] //角度方向
        );
        // 循环使用
//        annotation.setArrowLength(KLINE_ANNO_LENGTH[lengthPointer]);
        annotation.setArrowLength(15);
        lengthPointer = (lengthPointer + 1) % KLINE_ANNO_LENGTH.length;
        // 样式设置
        annotation.setArrowPaint(Color.BLACK);
        annotation.setTextAnchor(TextAnchor.BOTTOM_CENTER);
        // 新增标注边框增强可读性
        annotation.setOutlinePaint(new Color(0, 0, 0, 100));
        annotation.setOutlineStroke(new BasicStroke(0.8f));
        annotation.setFont(new Font("Microsoft YaHei", Font.BOLD, 10));
        if ("买入".equals(record.getOperation())) {
            annotation.setPaint(new Color(50, 168, 82)); // 绿色
        } else {
            annotation.setPaint(new Color(220, 53, 69)); // 红色
        }
        annotation.setBackgroundPaint(new Color(255, 255, 255, 180)); // 半透明白底
        return annotation;
    }

    // 创建K线数据集 [6,7](@ref)
    private static OHLCDataset createKLineDataset(List<KLine> kLines, String...dateF) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        if(dateF!=null&&dateF.length>0){
            sdf = new SimpleDateFormat(dateF[0]);
        }
        // 创建 OHLCDataItem 数组
        OHLCDataItem[] items = new OHLCDataItem[kLines.size()];

        for (int i = 0; i < kLines.size(); i++) {
            KLine k = kLines.get(i);
            Date date = sdf.parse(k.getDate());
            // 构造 OHLCDataItem：日期, 开盘价, 最高价, 最低价, 收盘价, 成交量
            items[i] = new OHLCDataItem(
                    date, k.getOpen(), k.getHigh(), k.getLow(), k.getClose(), k.getVolume()
            );
        }
        // 使用构造方法：传入数据系列名称和数组
        return new DefaultOHLCDataset("价格K线", items);
    }

    // 创建资产数据集
    private static XYDataset createAssetDataset(List<TradeRecord> records, TotalReturn totalReturn, String...dateF) throws ParseException {
        StringBuilder sb = new StringBuilder();
        sb.append("初始资金: ￥").append(totalReturn.getInitialCapital()).append(" ");
        sb.append("最终资金: ￥").append(totalReturn.getFinalCapital()).append(" ");
        sb.append("总收益率: ").append(totalReturn.getReturnRate()).append(" ");
        sb.append("交易次数: ").append(totalReturn.getTradeCount());

        TimeSeries assetSeries = new TimeSeries(sb.toString());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        if(dateF!=null&&dateF.length>0){
            sdf = new SimpleDateFormat(dateF[0]);
        }
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 统一为北京时间

        for (TradeRecord record : records) {
            Date date = sdf.parse(record.getDate());
            try {
                assetSeries.addOrUpdate(new Day(date), record.getTotalAsset());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        return new TimeSeriesCollection(assetSeries);
    }

    // 创建组合图表结构 [1,8]
    private static JFreeChart createCombinedChart(XYDataset assetDataset,
                                                  OHLCDataset kLineDataset,
                                                  String title,
                                                  List<KLine> kLines,
                                                  String...dateF) throws ParseException {
        // 1. 创建K线图子图
        XYPlot kLinePlot = new XYPlot();
        kLinePlot.setDataset(kLineDataset);

        // 设置K线渲染器
        CandlestickRenderer candleRenderer = new ColorVolumeRenderer(kLineDataset);
        // 配置渲染器
        candleRenderer.setUpPaint(Color.RED);      // 上涨蜡烛颜色
        candleRenderer.setDownPaint(Color.GREEN);  // 下跌蜡烛颜色
        candleRenderer.setDrawVolume(true);        // 显示成交量柱
        candleRenderer.setCandleWidth(0.8);        // 蜡烛宽度
        candleRenderer.setSeriesOutlinePaint(0, Color.DARK_GRAY); // 成交量柱边框
        kLinePlot.setRenderer(candleRenderer);

        // 设置K线图坐标轴
        DateAxis dateAxis = new DateAxis("日期");
        SimpleDateFormat df = new SimpleDateFormat("MM/dd");
        if(dateF!=null&&dateF.length>1){
            df = new SimpleDateFormat(dateF[1]);
        }
        dateAxis.setDateFormatOverride(df);
        dateAxis.setVerticalTickLabels(true); // 竖排标签
        dateAxis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); // 刻度居中

        long startMillis = DateUtils.parseDate(kLines.get(0).getDate(), "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss").getTime();
        long endMillis = DateUtils.parseDate(kLines.get(kLines.size()-1).getDate(), "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss").getTime();
        dateAxis.setRange(startMillis, endMillis); // 固定显示范围[6]
        kLinePlot.setDomainAxis(dateAxis);

        NumberAxis priceAxis = new NumberAxis("价格");
        kLinePlot.setRangeAxis(priceAxis);

        // 2. 创建资产子图
        XYPlot assetPlot = new XYPlot();
        assetPlot.setDataset(assetDataset);
//        DateAxis assetDomainAxis = new DateAxis();
//        assetDomainAxis.setRange(startMillis, endMillis); // 与K线图范围一致
        assetPlot.setDomainAxis(dateAxis); // 绑定到资产子图

        // 设置资产图渲染器
        XYLineAndShapeRenderer assetRenderer = new XYLineAndShapeRenderer(true, false);
        assetRenderer.setSeriesPaint(0, new Color(30, 144, 255)); // 深蓝色
        assetRenderer.setSeriesStroke(0, new BasicStroke(2.0f));
        assetPlot.setRenderer(assetRenderer);

        // 设置资产图坐标轴
        NumberAxis assetAxis = new NumberAxis("资产金额");
        assetAxis.setNumberFormatOverride(NumberFormat.getCurrencyInstance(Locale.CHINA));
        assetPlot.setRangeAxis(assetAxis);

        // 3. 创建组合图表 (共享时间轴) [1](@ref)
        CombinedDomainXYPlot combinedPlot = new CombinedDomainXYPlot(dateAxis);
        combinedPlot.add(kLinePlot, 3);  // K线图占60%高度
        combinedPlot.add(assetPlot, 2);  // 资产图占40%高度
        combinedPlot.setGap(10);         // 子图间距

        // 4. 创建完整图表
        JFreeChart chart = new JFreeChart(title, new Font("Microsoft YaHei", Font.BOLD, 18), combinedPlot, true);

        // 添加网格线和背景
        kLinePlot.setDomainGridlinePaint(new Color(200, 200, 200, 100));
        kLinePlot.setRangeGridlinePaint(new Color(200, 200, 200, 100));
        assetPlot.setDomainGridlinePaint(new Color(200, 200, 200, 100));
        assetPlot.setRangeGridlinePaint(new Color(200, 200, 200, 100));

        return chart;
    }

    // 添加交易标注到资产子图
    private static void addTradeAnnotations(JFreeChart chart, List<TradeRecord> records, String...dateF) throws ParseException {
        // 获取资产子图（第二个子图）
        CombinedDomainXYPlot combinedPlot = (CombinedDomainXYPlot) chart.getPlot();
        XYPlot assetPlot = (XYPlot) combinedPlot.getSubplots().get(1);

        double maxY = 0;
        double minY = Double.MAX_VALUE;

        // 计算Y轴范围
        for (TradeRecord record : records) {
            if (record.getTotalAsset() > maxY) maxY = record.getTotalAsset();
            if (record.getTotalAsset() < minY) minY = record.getTotalAsset();
        }
        double range = maxY - minY;

        Map<String, List<XYAnnotation>> dateAnnotations = new HashMap<>();

        // 创建所有买卖点标注
        for (TradeRecord record : records) {
            if ("买入".equals(record.getOperation()) || "卖出".equals(record.getOperation())) {
                String dateStr = record.getDate().substring(5);

                // 创建引导线标注
                XYPointerAnnotation annotation = createTradeAnnotation(record);

                // 检查同一天是否有多个交易
                if (dateAnnotations.containsKey(dateStr)) {
                    List<XYAnnotation> sameDay = dateAnnotations.get(dateStr);
                    sameDay.add(annotation);

                    // 计算偏移量避免重叠
                    int index = sameDay.size() - 1;
                    double yOffset = range * 0.05 * (index % 2 == 0 ? 1 : -1) * Math.ceil(index/2.0);
                    annotation.setY(record.getTotalAsset() + yOffset);

                    // 水平偏移
                    double xOffset = index * 15;
                    if(dateF!=null&&dateF.length>0){
                        annotation.setX(DateUtils.parseDate(record.getDate(), dateF[0]).getTime() + xOffset);
                    }else{
                        annotation.setX(DateUtils.parseDate(record.getDate(), "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss").getTime() + xOffset);
                    }
                } else {
                    List<XYAnnotation> newList = new ArrayList<>();
                    newList.add(annotation);
                    dateAnnotations.put(dateStr, newList);
                }

                assetPlot.addAnnotation(annotation);
            }
        }

        // 添加峰值标记
        addPeakAnnotation(assetPlot, records);
    }

    private static int anglePointer = 0;
    private final static double[] ANGLES = {Math.PI/2,Math.PI/2*3};// 90度角 270度方向

    private static XYPointerAnnotation createTradeAnnotation(TradeRecord record) throws ParseException {
        // 简化的标注文本
        XYPointerAnnotation pointerAnn = new XYPointerAnnotation(
                String.format("%s %s\n¥%.2f",
                        record.getDate().substring(5).replaceAll("-", "/"),
                        record.getOperation().charAt(0),
                        record.getPrice()),
                DateUtils.parseDate(record.getDate(), "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss").getTime(),
                record.getTotalAsset(),
                ANGLES[anglePointer] //角度方向
        );

        // 循环使用角度
        anglePointer = (anglePointer + 1) % ANGLES.length;

        pointerAnn.setArrowLength(25);
        pointerAnn.setLabelOffset(15);
        pointerAnn.setFont(new Font("Microsoft YaHei", Font.BOLD, 10));
        pointerAnn.setTextAnchor(TextAnchor.TOP_CENTER);

        // 设置交易类型颜色
        if ("买入".equals(record.getOperation())) {
            pointerAnn.setPaint(new Color(50, 168, 82)); // 绿色
        } else {
            pointerAnn.setPaint(new Color(220, 53, 69)); // 红色
        }
        pointerAnn.setArrowPaint(pointerAnn.getPaint());
        pointerAnn.setBackgroundPaint(new Color(255, 255, 255, 180));

        return pointerAnn;
    }

    // 添加峰值标记
    private static void addPeakAnnotation(XYPlot plot, List<TradeRecord> records) throws ParseException {
        TradeRecord peak = null;
        for (TradeRecord record : records) {
            if (peak == null || record.getTotalAsset() > peak.getTotalAsset()) {
                peak = record;
            }
        }

        if (peak != null) {
            XYTextAnnotation peakAnnotation = new XYTextAnnotation(
                    String.format("峰值: ¥%.2f\n%s",
                            peak.getTotalAsset(),
                            peak.getDate().substring(5).replace("-", "/")
                    ),
                    DateUtils.parseDate(peak.getDate(), "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss").getTime(),
                    peak.getTotalAsset() * 1.05
            );

            peakAnnotation.setFont(new Font("Microsoft YaHei", Font.BOLD, 12));
            peakAnnotation.setPaint(new Color(255, 193, 7)); // 金色
            peakAnnotation.setTextAnchor(TextAnchor.BOTTOM_CENTER);
            peakAnnotation.setBackgroundPaint(new Color(33, 37, 41, 200));

            plot.addAnnotation(peakAnnotation);
        }
    }

    // 应用中文主题
    private static void applyChineseTheme(JFreeChart chart) {
        StandardChartTheme theme = (StandardChartTheme) StandardChartTheme.createJFreeTheme();
        theme.setExtraLargeFont(new Font("Microsoft YaHei", Font.BOLD, 20));
        theme.setLargeFont(new Font("Microsoft YaHei", Font.BOLD, 15));
        theme.setRegularFont(new Font("Microsoft YaHei", Font.PLAIN, 13));
        theme.setSmallFont(new Font("Microsoft YaHei", Font.PLAIN, 11));
        theme.apply(chart);

        // 设置标题位置
        chart.getTitle().setHorizontalAlignment(HorizontalAlignment.CENTER);
    }

    // 保存组合图表
    private static void saveCombinedChart(String stockCode, JFreeChart chart,
                                          String startDate,
                                          String endDate) throws IOException {
        String dirPath = PARENT_PATH + "/backtest/"+stockCode;
        File dir = new File(dirPath);
        if(!dir.exists()){
            dir.mkdir();
        }
        ChartUtils.saveChartAsPNG(
                new File(dirPath+"/"+startDate+"-"+endDate+"_chart.png"),
                chart,
                2240,
                1400  // 高分辨率适应复杂图表
        );
    }

}
